package Dm

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"db2s/ETL"
	"db2s/global"
	"db2s/parDef"
	"db2s/ref"
	"reflect"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

func resultTypeCheck(e, s string, r any) (err error) {
	switch s {
	case "map":
		if !strings.EqualFold(fmt.Sprintf("%v", reflect.TypeOf(r)), "map[string]interface {}") {
			err = errors.New(fmt.Sprintf("%v result type not is map[string]interface{}, it is %v", e, reflect.TypeOf(r)))
		}
	case "sMap":
		if !strings.EqualFold(fmt.Sprintf("%v", reflect.TypeOf(r)), "[]map[string]interface {}") {
			err = errors.New(fmt.Sprintf("%v result type not is []map[string]interface{}, it is %v", e, reflect.TypeOf(r)))
		}
	default:
		err = errors.New(fmt.Sprintf("input type "))
	}
	return
}
func isNumeric(s string) bool {
	// 正则表达式：^ 表示开始，$ 表示结束，\d 表示数字，\. 表示小数点
	re := regexp.MustCompile(`^[\d.]+$`)
	return re.MatchString(s)
}
func rowsLimitC(limit int) (p string) {
	if limit > 0 {
		p = fmt.Sprintf(" and rownum <= %v", limit)
	}
	return
}
func tableObject(schema, table, partition string) (p string) {
	p = fmt.Sprintf(" \"%v\".\"%v\" T ", schema, table)
	if len(partition) > 0 && !strings.EqualFold(partition, "single") {
		p = fmt.Sprintf(" \"%v\".\"%v\" PARTITION(%v) T ", schema, table, partition)
	}
	return
}
func colAndWhere(s global.StartPart) (res colAndWhereResult, err error) {
	var (
		tableSql = TableSql{}
		event    = "[oracleStartPart]"
	)
	if res.selectColumn, res.asSelectColumn, err = tableSql.SelectColumn(s.SelectColumn); err != nil || len(res.selectColumn) == 0 {
		err = errors.New(fmt.Sprintf("%v get sql select Column fail. Execution process:{chema:%v,table:%v,select column:%v,err:%v}", event, s.TableInfo.Schema, s.TableInfo.BackendTableName, s.SelectColumn, err))
		return
	}
	if res.whereCondition, err = tableSql.WhereGenerate(s.WhereGenerate); err != nil {
		err = errors.New(fmt.Sprintf("%v get sql select where Condition fail. Execution process:{chema:%v,table:%v,select where Condition:%v,err:%v}", event, s.TableInfo.Schema, s.TableInfo.BackendTableName, s.WhereGenerate, err))
		return
	}
	if res.orderByColumn, err = tableSql.OrderBY(s.WhereGenerate); err != nil {
		return
	}
	return
}
func whereFilter(where string) (p string) {
	if len(where) > 0 {
		p = fmt.Sprintf(" and %v", where)
	}
	return
}
func specifyScn(s string) (p string) {
	if !strings.EqualFold(s, "0") && len(s) > 0 {
		p = fmt.Sprintf("AS OF SCN %v", s)
	}
	return
}
func parallel(s string) (p string) {
	if len(s) > 0 {
		return fmt.Sprintf("/*+ parallel(%s) */", s)
	}
	return
}
func stringNullActive(t any) (r any) {
	switch fmt.Sprintf("%v", t) {
	case "<null>", "<entry>":
		r = "NULL"
	default:
		r = fmt.Sprintf("%v", t)
	}
	return
}
func mapNullActive(s map[string]any) (p map[string]any) {
	p = make(map[string]any)
	for q, t := range s {
		switch fmt.Sprintf("%v", t) {
		case "<null>", "<entry>":
			p[q] = "NULL"
		default:
			p[q] = fmt.Sprintf("%v", t)
		}
	}
	return
}
func mapAnyToString(s map[string]any) (p map[string]string) {
	r := mapNullActive(s)
	p = make(map[string]string)
	for q, t := range r {
		switch fmt.Sprintf("%v", t) {
		case "<null>", "<entry>":
			p[q] = "NULL"
		default:
			p[q] = fmt.Sprintf("%v", t)
		}
	}
	return
}
func mapStringToStruct(s, p any) (err error) {
	var jsonData []byte
	jsonData, err = json.Marshal(s)
	if err != nil {
		return
	}
	// 反序列化JSON字符串到结构体
	err = json.Unmarshal(jsonData, p)
	if err != nil {
		return
	}
	return
}
func legitimateIf(event, sqlStr string, s any, active ETL.InstanceActiveS) error {
	var (
		schema, table string
		db            map[string]*sql.DB
	)
	switch fmt.Sprintf("%v", reflect.TypeOf(s)) {
	case "global.TableSumInput":
		r := s.(global.TableSumInput)
		schema = r.TableInfo.Schema
		table = r.TableInfo.BackendTableName
		db = r.Db
	case "global.StartPart":
		r := s.(global.StartPart)
		schema = r.TableInfo.Schema
		table = r.TableInfo.BackendTableName
		db = r.DB
	case "global.RowsDataInput":
		r := s.(global.RowsDataInput)
		schema = r.TableInfo.Schema
		table = r.TableInfo.BackendTableName
		db = r.DB
	}
	if active.DB == nil || len(schema) == 0 || len(table) == 0 || len(db) == 0 {
		return errors.New(fmt.Sprintf("%v sql exec fail. Execution process:{schema:%v,table:%v,sql:%v,shard:%v,db:%v}", event, schema, table, sqlStr, "single", db))
	}
	return nil
}

func ObjectNameToUpper(objectName string) (res string) {
	res = objectName
	if strings.HasPrefix(res, "\"") && strings.HasSuffix(res, "\"") {
		return
	}
	return strings.ToUpper(res)
}

func funcProcTrim(body string) (res string) {
	res = body
	if strings.Contains(body, "(") && strings.HasSuffix(body, ")") {
		if n := strings.Index(strings.ToLower(body), "("); n != -1 {
			res = body[:n]
		}
	}
	return
}

type execSqlS struct {
	DB        map[string]*sql.DB
	ShardName string
	Input     global.StartPartConfigInputP
}

func getExecSqlData(s any) (x execSqlS) {
	switch s.(type) {
	case global.TablesMetaInfoInput:
		p := s.(global.TablesMetaInfoInput)
		x = execSqlS{
			DB:    p.DB,
			Input: p.Input,
		}
	case global.SchemaObjectInput:
		p := s.(global.SchemaObjectInput)
		x = execSqlS{
			DB:    p.DB,
			Input: p.Input,
		}
	case global.GetColumnTypeInput:
		p := s.(global.GetColumnTypeInput)
		x = execSqlS{
			DB:    p.DB,
			Input: p.Input,
		}
	case global.TableSumInput:
		p := s.(global.TableSumInput)
		x = execSqlS{
			DB:    p.Db,
			Input: p.Input,
		}
	case global.RowsDataInput:
		p := s.(global.RowsDataInput)
		x = execSqlS{
			DB:    p.DB,
			Input: p.Input,
		}
	case global.DBGetCharacterS:
		p := s.(global.DBGetCharacterS)
		x = execSqlS{DB: p.DB, Input: p.Input}
	case global.StartPart:
		p := s.(global.StartPart)
		x = execSqlS{DB: p.DB, Input: p.Input}
	case global.DbParameterOptimizerS:
		p := s.(global.DbParameterOptimizerS)
		x = execSqlS{
			DB:        p.DB,
			Input:     p.Input,
			ShardName: p.ShardName,
		}
	case conDbQuery:
		p := s.(conDbQuery)
		x = execSqlS{
			DB:        p.DB,
			ShardName: p.ShardName,
			Input:     p.Input,
		}
	}
	return
}

type ExecSqlInputS struct {
	Ss           any
	Event        string
	SelectSqlStr string
	UpdateSqlStr []string
	InsertSqlStr []*string
	Expect       string
	ShardName    string
}

func ExecSelectSql(e ExecSqlInputS) (timeout int64, res any, err error) {
	var (
		x               = getExecSqlData(e.Ss)
		expectTypeValue string
	)
	if len(e.ShardName) == 0 {
		e.ShardName = "single"
	}
	active := ETL.InstanceActiveS{DBType: DBType, DB: x.DB[e.ShardName], StopTime: x.Input.SqlExecStopTime, SqlMode: true}
	if len(x.DB) == 0 || active.DB == nil {
		err = errors.New(fmt.Sprintf("%v sql exec fail. Execution process:{sql:%v,shard:%v,db:%v,err:%v}", e.Event, e.SelectSqlStr, e.ShardName, x.DB, err))
		return
	}
	if timeout, res, err = active.SqlQuery(e.SelectSqlStr, e.Expect); err != nil || res == nil {
		if err != nil {
			err = errors.New(fmt.Sprintf("%v sql exec fail. Execution process:{sql:%v,err:%v}", e.Event, e.SelectSqlStr, err))
		}
		return
	}
	switch e.Expect {
	case ETL.Map:
		expectTypeValue = "map[string]interface {}"
	case ETL.StringGarth:
		expectTypeValue = "[]string"
	case ETL.String:
		expectTypeValue = "string"
	case ETL.StringPoint:
		expectTypeValue = "*string"
	case ETL.StringPointGarth:
		expectTypeValue = "[]*string"
	case ETL.GarthStringPointGarth:
		expectTypeValue = "[][]*string"
	case ETL.SMap:
		expectTypeValue = "[]map[string]interface {}"
	case ETL.FlowAny:
		expectTypeValue = "chan interface {}"
	case ETL.FlowString:
		expectTypeValue = "chan string"
	}
	if !strings.EqualFold(fmt.Sprintf("%v", reflect.TypeOf(res)), expectTypeValue) {
		err = errors.New(fmt.Sprintf("%v sql exec fail. result type mismatch! return:%v want:%v.process:{sql:%v,result:%v}", e.Event, reflect.TypeOf(res), expectTypeValue, e.SelectSqlStr, res))
		return
	}
	return
}

func newGetExecSqlData(s any) (x newExecSqlS, err error) {
	switch s.(type) {
	case Parameter:
		p := s.(Parameter)
		x = newExecSqlS{
			DB:      p.Con,
			options: p.Options,
		}
	case parDef.Parameter:
		p := s.(parDef.Parameter)
		x = newExecSqlS{
			DB: p.Con,
			options: Options{
				Scn:      p.Options.Scn,
				StopTime: p.Options.StopTime,
				WhereSql: p.Options.WhereSql,
				//WhereAdd: p.Options.WhereAdd,
			},
		}
	default:
		err = ref.ErrAddPrintf("newGetExecSqlData", errors.New(fmt.Sprintf("type not match. curry type is %v", reflect.TypeOf(s))))
	}
	return
}
func newExecSelectSql(e ExecSqlInputS) (timeout int64, res any, err error) {
	var (
		x               newExecSqlS
		event           = "[newExecSelectSql]"
		expectTypeValue string
	)
	if x, err = newGetExecSqlData(e.Ss); err != nil {
		err = ref.ErrAddPrintf(event, err)
		return
	}
	if len(e.ShardName) == 0 {
		e.ShardName = "single"
	}
	active := ETL.InstanceActiveS{DBType: DBType, DB: x.DB, FixRows: x.options.FixRows, StopTime: x.options.StopTime, SqlMode: true}
	if active.DB == nil {
		err = ref.ErrAddPrintf(event, errors.New(fmt.Sprintf("db connection is nil.")))
		return
	}
	if timeout, res, err = active.SqlQuery(e.SelectSqlStr, e.Expect); err != nil {
		err = ref.ErrAddPrintf(event, err)
		return
	}
	switch e.Expect {
	case ETL.Map:
		expectTypeValue = "map[string]interface {}"
	case ETL.StringGarth:
		expectTypeValue = "[]string"
	case ETL.String:
		expectTypeValue = "string"
	case ETL.StringPoint:
		expectTypeValue = "*string"
	case ETL.StringPointGarth:
		expectTypeValue = "[]*string"
	case ETL.GarthStringPointGarth:
		expectTypeValue = "[][]*string"
	case ETL.SMap:
		expectTypeValue = "[]map[string]interface {}"
	case ETL.FlowAny:
		expectTypeValue = "chan interface {}"
	case ETL.FlowString:
		expectTypeValue = "chan string"
	}
	if !strings.EqualFold(fmt.Sprintf("%v", reflect.TypeOf(res)), expectTypeValue) {
		err = ref.ErrAddPrintf(event, errors.New(fmt.Sprintf("result type not match! return:%v want:[]string.process:{sql:%v,result:%v}", reflect.TypeOf(res), e.SelectSqlStr, res)))
		return
	}
	return
}
func QueryColValManage(val any) (newVal any) {
	if val == nil || len(fmt.Sprintf("%v", val)) == 0 || strings.EqualFold(fmt.Sprintf("'%v'", val), "'<nil>'") {
		switch {
		case len(fmt.Sprintf("%v", val)) == 0:
			newVal = "<entry>" //空字符串
		}
		newVal = "<null>"
		return
	} else {
		newVal = val
	}
	switch val.(type) {
	case string:
		if val == nil {
			return
		}
		switch {
		case strings.Contains(val.(string), "SRID:") && strings.Contains(val.(string), "AXIS:EPSG"): //wkt模式
			var err error
			if newVal, err = GeomWktToJson(val); err != nil {
				return err
			}
		default:
			return newVal
		}
	default:
		return newVal
	}
	return
}
func ExecUpdateSql(e ExecSqlInputS) (timeout int64, res any, err error) {
	var (
		x = getExecSqlData(e.Ss)
	)
	if len(e.ShardName) == 0 {
		e.ShardName = "single"
	}
	active := ETL.InstanceActiveS{DBType: DBType, DB: x.DB[e.ShardName], FixRows: x.Input.FixRows, StopTime: x.Input.SqlExecStopTime, SqlMode: x.Input.SqlMode}
	if len(x.DB) == 0 || active.DB == nil {
		err = errors.New(fmt.Sprintf("sql exec fail. Execution process:{sql:%v,shard:%v,db:%v,err:%v}", e.SelectSqlStr, e.ShardName, x.DB, err))
		return
	}
	if _, err = active.Update(e.UpdateSqlStr); err != nil {
		err = errors.New(fmt.Sprintf("sql exec fail. Execution process:{sql:%v,shard:%v,db:%v,err:%v}", e.SelectSqlStr, e.ShardName, x.DB, err))
		return
	}
	return
}
func newExecInsertSql(e ExecSqlInputS) (result global.Return, err error) {
	var (
		x     newExecSqlS
		event = "[newExecUpdateSql]"
	)
	if x, err = newGetExecSqlData(e.Ss); err != nil {
		err = ref.ErrAddPrintf(event, err)
	}
	var res ETL.FnReturn
	defer func() {
		if r := recover(); r != nil {
			stack := make([]byte, 4096)
			length := runtime.Stack(stack, true)
			err = ref.ErrAddPrintf("newExecBathInsert", errors.New(fmt.Sprintf("recovered from panic: %v 异常位置: %s\n", r, stack[:length])))
		}
	}()
	active := ETL.WriteOpt{DBType: DBType, DB: x.DB, FixRows: x.options.FixRows, StopTime: x.options.StopTime, SqlMode: true}
	if active.DB == nil {
		err = errors.New(fmt.Sprintf("sql exec fail. Execution process:{sql:%v,shard:%v,db:%v,err:%v}", e.SelectSqlStr, e.ShardName, x.DB, err))
		return
	}
	if res = ETL.Insert(active, []string{}, e.InsertSqlStr); res.Err != nil {
		err = ref.ErrAddPrintf("newExecInsertSql", res.Err)
		return
	}
	result.Result = res.Result
	result.TimeOut = res.ExecTime
	err = res.Err
	return
}
func forExecQuerySql(event string, parameter any, Expect string, execSql []string) (result global.Return, err error) {
	defer func() {
		if err != nil {
			err = ref.ErrAddPrintf("forExecQuerySql", err)
		}
	}()
	for _, v := range execSql {
		result.Sql = v
		if result.TimeOut, result.Result, err = newExecSelectSql(ExecSqlInputS{
			Ss:           parameter,
			Event:        event,
			SelectSqlStr: result.Sql,
			Expect:       Expect,
		}); err != nil || result.Result == nil {
			continue
		} else {
			return
		}
	}
	return
}
func stringAnyToInt64(s any) (res int64, err error) {
	switch pp := stringNullActive(s); pp.(type) {
	case string:
		if !strings.EqualFold(pp.(string), "NULL") {
			return strconv.ParseInt(fmt.Sprintf("%v", pp), 10, 64)
		}
	default:
		err = ref.ErrAddPrintf("stringAnyToInt64", errors.New(fmt.Sprintf("type is not match. curry type is %v", reflect.TypeOf(s))))
	}
	return
}
func stringAnyToFloat64(s any) (res float64, err error) {
	switch pp := stringNullActive(s); pp.(type) {
	case string:
		if !strings.EqualFold(pp.(string), "NULL") {
			return strconv.ParseFloat(fmt.Sprintf("%v", pp), 64)
		}
	}
	return
}
